﻿using System;
using System.Linq;
using ClaySharp;
using ClaySharp.Behaviors;

namespace Sail.UI.Zones
{
    public class ZoneHoldingBehavior : ClayBehavior
    {
        private readonly Func<dynamic> _zoneFactory;

        public ZoneHoldingBehavior(Func<dynamic> zoneFactory)
        {
            _zoneFactory = zoneFactory;
        }

        public override object GetMember(Func<object> proceed, object self, string name)
        {
            if (name == "Zones")
            {
                return ClayActivator.CreateInstance(new IClayBehavior[]
                                                        {
                                                            new InterfaceProxyBehavior(),
                                                            new ZonesBehavior(_zoneFactory, self)
                                                        });
            }

            var result = proceed();
            if (((dynamic) result) == null)
            {
                return ClayActivator.CreateInstance(new IClayBehavior[]
                                                        {
                                                            new InterfaceProxyBehavior(),
                                                            new NilBehavior(),
                                                            new ZoneOnDemandBehavior(_zoneFactory, self, name)
                                                        });
            }
            return result;
        }

        public class ZonesBehavior : ClayBehavior
        {
            private readonly Func<dynamic> _zoneFactory;
            private readonly object _parent;

            public ZonesBehavior(Func<dynamic> zoneFactory, object parent)
            {
                _zoneFactory = zoneFactory;
                _parent = parent;
            }

            public override object GetMember(Func<object> proceed, object self, string name)
            {
                var parentMember = ((dynamic) _parent)[name];
                if (parentMember == null)
                {
                    return ClayActivator.CreateInstance(new IClayBehavior[]
                                                            {
                                                                new InterfaceProxyBehavior(),
                                                                new NilBehavior(),
                                                                new ZoneOnDemandBehavior(_zoneFactory, _parent, name)
                                                            });
                }
                return parentMember;
            }

            public override object GetIndex(Func<object> proceed, object self,
                                            System.Collections.Generic.IEnumerable<object> keys)
            {
                if (keys.Count() == 1)
                {
                    return GetMember(proceed, null, System.Convert.ToString(keys.Single()));
                }
                return proceed();
            }
        }

        public class ZoneOnDemandBehavior : ClayBehavior
        {
            private readonly Func<dynamic> _zoneFactory;
            private readonly object _parent;
            private readonly string _potentialZoneName;

            public ZoneOnDemandBehavior(Func<dynamic> zoneFactory, object parent, string potentialZoneName)
            {
                _zoneFactory = zoneFactory;
                _parent = parent;
                _potentialZoneName = potentialZoneName;
            }

            public override object InvokeMember(Func<object> proceed, object self, string name,
                                                INamedEnumerable<object> args)
            {
                var argsCount = args.Count();
                if (name == "Add" && (argsCount == 1 || argsCount == 2))
                {
                    if (args.First() == null)
                        return _parent;

                    dynamic parent = _parent;

                    dynamic zone = _zoneFactory();
                    zone.Parent = _parent;
                    zone.ZoneName = _potentialZoneName;
                    parent[_potentialZoneName] = zone;

                    if (argsCount == 1)
                        return zone.Add(args.Single());

                    return zone.Add(args.First(), (string) args.Last());
                }
                return proceed();
            }
        }
    }
}